-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MathProgBase solver interface. #9
Conversation
Looks reasonable. You should run the relevant functions in But I'd add that the important MOI infrastructure for QPs is now in place in JuMP, and we could use some early testers. |
src/OSQPSolverInterface.jl
Outdated
settings::Dict{Symbol,Any} | ||
end | ||
|
||
OSQPSolver() = OSQPSolver(Dict{Symbol, Any}()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe name this OSQPMPBSolver
to avoid the imminent conflict with MOI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this is still the common approach in most of the current MathProgBase interfaces, e.g., EcosSolverInterface.jl.
@mlubin Are you planning to change the other solvers as well or to just drop the MathProgBase interfaces altogether?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MPB interfaces will all be moved into separate packages when we start releasing the new MOI interfaces. It's up to developer discretion whether the MOI interface will sit in the same package as the direct solver wrapper or in a separate package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it could be better to move this interface to a separate package called MathProgBaseOSQP.jl
as done in MathProgBaseMosek.jl. In this way we could keep OSQPSolver
in both MPB and MOI interfaces and avoid using both them at the same time. Is this the purpose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a bit silly to create a package just for 200 lines of code that are going to be deprecated soon. (I'm reconsidering if we should do this for all solvers.) If we remove the line:
import .OSQPSolverInterface: OSQPSolver
then there's nothing much to worry about with having both MPB and MOI interfaces in the same package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed the module to OSQPMathProgBaseInterface
(solver is still named OSQPSolver
). No longer exporting OSQPSolver
, but exporting OSQPMathProgBaseInterface
instead. What do you think?
Thank you for this. I guess the next steps are to:
|
0d9ac6c
to
c27d244
Compare
Since OSQP doesn't support quadratic constraints, neither |
Codecov Report
@@ Coverage Diff @@
## master #9 +/- ##
==========================================
+ Coverage 71.69% 79.91% +8.22%
==========================================
Files 3 4 +1
Lines 272 488 +216
==========================================
+ Hits 195 390 +195
- Misses 77 98 +21
Continue to review full report at Codecov.
|
test/mpbinterface.jl
Outdated
sol = quadprog([0., 0., 0.],[2. 1. 0.; 1. 2. 1.; 0. 1. 2.],[1. 2. 3.; 1. 1. 0.],'>',[4., 1.],-Inf,Inf,solver) | ||
@test sol.status == :Optimal | ||
@test isapprox(sol.objval, 130/70, atol=1e-6) | ||
@test isapprox(norm(sol.sol[1:3] - [0.5714285714285715,0.4285714285714285,0.8571428571428572]), 0.0, atol=1e-6) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised the test passes with these tolerances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, missed the setting for eps_abs above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, polish = true
is more essential. Just setting eps_abs
to even 1e-8
results in test failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Polish shouldn't be necessary, but you may need to set eps_rel
(osqp/osqp#40) to a tiny value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you take the relative tolerance into account it might work without polishing:
@test isapprox(norm(sol.sol[1:3] - [0.5714285714285715,0.4285714285714285,0.8571428571428572]), rtol=1e-6, atol=1e-6)
REQUIRE
Outdated
@@ -1,3 +1,4 @@ | |||
BinDeps | |||
julia 0.6 | |||
Compat | |||
MathProgBase 0.7 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's always a good idea to put an upper bound on the MPB requirement.
Depends on whether you want to call OSQP on JuMP models that have variables with bounds. |
I think people use variable bounds a lot. We could keep track of them in the problem data as
Then, when we call |
Only allow :Min as the sense for now, as the current implementation can lead to confusing/erroneous behavior. Add freemodel!, copy, getsense, setsense!.
Re: #9 (comment): I started doing that initially, but I got a little worried that the interpretation of the dual variables would become confusing/nonstandard. We also need better handling of |
JuMP has lots of tests for duals on LPs with both min/max sense. Not sure if there are specific tests for QPs. You should really feel free to only implement the parts that you need, because all of this is going to be tossed out after JuMP 0.18. Rejecting |
d849131
to
ff8f248
Compare
Improved test coverage. Also discovered and fixed a minor problem with |
Don't merge yet; I suspect there's something wrong with the quadratic objective methods. |
Thanks for adding these tests. Tomorrow I will merge my changes as well to complete the interface. |
Fixed the objective bug and added yet more tests that displayed the problem. I'm successfully using OSQP through JuMP for my intended application now. |
…d to fix objective sense. Not tested yet
src/mpbinterface.jl
Outdated
end | ||
end | ||
|
||
function Base.resize!(model::OSQPModel, n, m) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the resize!
function really necessary? it seems that it does not properly resize the problem but it just creates zero matrices P
and A
and resizes the vectors. I guess this is needed in the loadproblem!
function where you call copy!
. Is this necessary/more efficient than just using copy
or deepcopy
for the data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR: answered on gitter. Yes, efficiency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, there will be quite a lot of allocations when using the additional functionality. You could add some fields to the model to avoid those allocations. Or (and that's absolutely fine) don't bother and we'll handle it properly in the MathOptInterface version.
test/runtests.jl
Outdated
# "primal_infeasibility.jl", | ||
# "unconstrained.jl", | ||
# "warm_start.jl", | ||
# "update_matrices.jl", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reenable
src/mpbinterface.jl
Outdated
module OSQPMathProgBaseInterface | ||
|
||
using OSQP: Model, Results, setup!, solve!, update!, clean!, update_settings!, warm_start! | ||
importall MathProgBase.SolverInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
importall
is going away soon. See https://github.com/JuliaLang/julia/blob/master/NEWS.md#language-changes.
src/mpbinterface.jl
Outdated
end | ||
end | ||
|
||
function get_qp_variables(model::OSQPMathProgModel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just have this be numvar
?
src/mpbinterface.jl
Outdated
return n | ||
end | ||
|
||
get_qp_constraints(model::OSQPMathProgModel) = size(model.A, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
numconstr
?
src/mpbinterface.jl
Outdated
checksolved(model::OSQPMathProgModel) = isdefined(model, :results) || error("Model has not been solved.") | ||
|
||
# Reset problem when setup has to be performed again | ||
resetproblem(model::OSQPMathProgModel) = (model.results = nothing; model.perform_setup=true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't work. model.results
can't store nothing
.
src/mpbinterface.jl
Outdated
|
||
# Negate cost if necessary | ||
if model.sense == :Max | ||
model.q *= -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allocation could be avoided, but not a huge deal.
src/mpbinterface.jl
Outdated
getconstrmatrix(model::OSQPMathProgModel) = model.A | ||
|
||
|
||
function addvar!(model::OSQPMathProgModel, constridx, constrcoef, l, u, objcoef) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of allocations, to the point that it may not be beneficial to support this. I don't think JuMP even uses this.
src/mpbinterface.jl
Outdated
@boundscheck length(colidx) == nterms || error() | ||
|
||
# Check if only the values have changed | ||
if isdefined(model, :P) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is always true.
src/mpbinterface.jl
Outdated
# Check if only the values have changed | ||
if isdefined(model, :P) | ||
Pi, Pj, Px = findnz(model.P) | ||
if (rowidx == Pi) & (colidx == Pj) & !model.perform_setup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that ordering of rowidx
and colidx
shouldn't matter and
Duplicate index sets (i,j) are accepted and will be summed together
this is kind of a crude check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have left it for now since I cannot find an easier way to check if the sparsity pattern of P stays the same. If you have any suggestions feel free to update it.
src/mpbinterface.jl
Outdated
|
||
# Change sign if maximizing | ||
if model.sense == :Max | ||
model.P *= -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expensive way of doing this.
Damn, I pushed before finishing some stuff!!! Sorry. I had to rush out to finish some other things. I wanted to rebase everything before pushing it. I will adjust those things tomorrow ok? (Have lots of IKEA stuff to put together...). Thanks anyway for the performance tips. I am not totally familiar with Julia performance guidelines yet. I haven't used it that much until very recently. |
I have fixed the interface. It should now support incremental model building, infeasibility certificates, Max or Min problems and problem data updates. There was a problem with the dual variables sign before. I have removed a couple of unnecessary allocations but I am quite sure we could do something better memory-wise. However, we might just leave it as it is and focus on performance improvements for the MathOptInterface. Let me know what you think. |
Looks good to me! |
Prep for Vector constraints.
I know MathOptInterface is coming soon, but I want to try this with JuMP right now.
No tests yet, but most things seem to be working. Demo code:
For ease of implementation, I decided to store copies of
P
,q
, etc. inOSQPModel
and callOSQP.setup!
inMathProgBase.optimize!
. None of the updating infrastructure is currently used. Still, it's a start.